package com.share.example.stock.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.share.common.common.Page;
import com.share.common.utils.*;
import com.share.example.stock.enums.StockStatusEnum;
import com.share.example.stock.mapper.StockBaseInfoMapper;
import com.share.example.stock.mapper.StockDayPriceMapper;
import com.share.example.stock.model.ak.InfoBjNameCode;
import com.share.example.stock.model.ak.InfoShNameCode;
import com.share.example.stock.model.ak.InfoSzNameCode;
import com.share.example.stock.model.ak.ProfileCninfo;
import com.share.example.stock.model.db.StockBaseInfo;
import com.share.example.stock.model.rsp.StockBaseInfoPageReq;
import com.share.example.stock.model.vo.DayMinMaxAvgSamp;
import com.share.example.stock.service.AkService;
import com.share.example.stock.service.IStockBaseInfoService;
import com.share.example.stock.service.ITradeDateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author author
 * @since 2023-06-10
 */
@Slf4j
@Service
public class StockBaseInfoServiceImpl extends ServiceImpl<StockBaseInfoMapper, StockBaseInfo> implements IStockBaseInfoService {
    //连续清理记错计数器
    private AtomicInteger errorCount = new AtomicInteger(0);

    private Map<String, StockBaseInfo> stockCodeMap = new HashMap<>();

    @Resource
    private StockBaseInfoMapper stockBaseInfoMapper;

    @Resource
    private StockDayPriceMapper stockDayPriceMapper;

    @Autowired
    private AkService akService;

    @Autowired
    private ITradeDateService iTradeDateService;

    @Override
    public StockBaseInfo getStockCodeMap(String code) {
        return stockCodeMap.get(code);
    }

    //重置错误id
    @Override
    public void resetErrorCount(int init) {
        errorCount.set(init);
    }

    //加载所有code,禁止重复
    @Override
    public Map<String, StockBaseInfo> initStockInfo() {
        QueryWrapper<StockBaseInfo> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("id");

        List<StockBaseInfo> tmpList = stockBaseInfoMapper.selectList(wrapper);
        Map<String, StockBaseInfo> tmpMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(tmpList)) {
            tmpList.forEach(e -> tmpMap.put(e.getCode(), e));
            if (tmpMap.size() > 0) {
                this.stockCodeMap = tmpMap;
            }
        }
        return tmpMap;
    }

    //计算并更新,前一日的均价
    @Override
    public int updateDn(String strDate) {
        String startDay5 = iTradeDateService.getHistoryDay(strDate, -6);
        String startDay10 = iTradeDateService.getHistoryDay(strDate, -11);
        String startDay20 = iTradeDateService.getHistoryDay(strDate, -21);
        String startDay60 = iTradeDateService.getHistoryDay(strDate, -61);

        //key是code
        Map<String, DayMinMaxAvgSamp> map5 = stockDayPriceMapper.calcPriceByDateNotContainStartAndEnd(startDay5, strDate);
        Map<String, DayMinMaxAvgSamp> map10 = stockDayPriceMapper.calcPriceByDateNotContainStartAndEnd(startDay10, strDate);
        Map<String, DayMinMaxAvgSamp> map20 = stockDayPriceMapper.calcPriceByDateNotContainStartAndEnd(startDay20, strDate);
        Map<String, DayMinMaxAvgSamp> map60 = stockDayPriceMapper.calcPriceByDateNotContainStartAndEnd(startDay60, strDate);

        int count = 0;
        for (String code : map60.keySet()) {
            StockBaseInfo record = new StockBaseInfo();
            record.setCode(code);
            record.setDnUpdateTime(new Date());

            DayMinMaxAvgSamp item = map5.get(code);
            if (item != null) {
                record.setD5VolumeAvg(item.getVolumeAvg());
                record.setD5Avg(item.getAvg());
                record.setD5Samp(item.getSamp());
                record.setD5MinDAvg(item.getMinDAvg());
                record.setD5MaxDAvg(item.getMaxDAvg());
                record.setD5MaxMinDMin(item.getMaxMinDMin());
            }

            item = map10.get(code);
            if (item != null) {
                record.setD10VolumeAvg(item.getVolumeAvg());
                record.setD10Avg(item.getAvg());
                record.setD10Samp(item.getSamp());
                record.setD10MinDAvg(item.getMinDAvg());
                record.setD10MaxDAvg(item.getMaxDAvg());
                record.setD10MaxMinDMin(item.getMaxMinDMin());
            }


            item = map20.get(code);
            if (item != null) {
                record.setD20VolumeAvg(item.getVolumeAvg());
                record.setD20Avg(item.getAvg());
                record.setD20Samp(item.getSamp());
                record.setD20MinDAvg(item.getMinDAvg());
                record.setD20MaxDAvg(item.getMaxDAvg());
                record.setD20MaxMinDMin(item.getMaxMinDMin());
            }

            item = map60.get(code);
            if (item != null) {
                record.setD60VolumeAvg(item.getVolumeAvg());
                record.setD60Avg(item.getAvg());
                record.setD60Samp(item.getSamp());
                record.setD60MinDAvg(item.getMinDAvg());
                record.setD60MaxDAvg(item.getMaxDAvg());
                record.setD60MaxMinDMin(item.getMaxMinDMin());
            }

            Map<String, Object> tmpMap = ObjUtil.toMapIgnoreNull(record);
            if (tmpMap != null && tmpMap.size() > 2) {
                count++;
                ThreadUtil.execute(() -> stockBaseInfoMapper.updateByCode(record)); //异步更新
            }
        }
        return count;
    }

    /**
     * 新股检测----每天都有新股,所以每天都要检测
     *
     * @param code
     * @return
     */
    @Override
    public int isNewCode(String code) {
        int re = 0;
        if (StringUtils.isNotEmpty(code)) {
            QueryWrapper<StockBaseInfo> wrapper = new QueryWrapper<>();
            wrapper.select("code");
            wrapper.eq("code", code);
            List<StockBaseInfo> tmpList = stockBaseInfoMapper.selectList(wrapper);
            if (tmpList.size() < 1) {
                StockBaseInfo record = new StockBaseInfo();
                record.setCode(code);
                record.setBasePullCount(0);
                if (code.startsWith("688") || code.startsWith("8") || code.startsWith("4")) {
                    record.setStatus(StockStatusEnum.RM_HISTORY.getCode());
                } else {
                    record.setStatus(StockStatusEnum.NORMAL.getCode());
                }
                re = stockBaseInfoMapper.insert(record);
            }
        }
        return re;
    }

    /**
     * 定时任务 拉取需要同步的code
     * 逻辑：
     * 1.拉取多次失败-直接跳过
     * 2.拉取成功的--看时间
     * 3.其他状态的直接拉取
     *
     * @return
     */
    @Override
    public List<String> listCodeByDayPullCount() {
        QueryWrapper<StockBaseInfo> wrapper = new QueryWrapper<>();
        wrapper.in("status", StockStatusEnum.getDayPullStatus()); //赛选有效的
        wrapper.orderByDesc("day_pull_count");
        wrapper.orderByAsc("day_update_time");
        List<String> codeList = new LinkedList<>();
        List<StockBaseInfo> tmpList = stockBaseInfoMapper.selectList(wrapper);
        for (StockBaseInfo dbBaseInfo : tmpList) {
            boolean isAdd = true;
            boolean insertFirst = true;

            Integer pullCount = dbBaseInfo.getDayPullCount();
            if (pullCount == null) {
                isAdd = true;
            } else if (pullCount <= -3) {
                //拉取多次都失败的,手动跳过即可,事后手动干预
                isAdd = false;
            } else if (pullCount >= 1) {
                //之前就拉取成功了,根据时间来判断时候,是否需要更新
                Date date = dbBaseInfo.getDayUpdateTime();
                if (date == null) {
                    isAdd = true;
                } else {
                    Date splitDate = DateExtUtil.addDay(new Date(), -15);
                    if (DateExtUtil.compareDateTime(splitDate, date) >= 0) {
                        isAdd = true;
                        insertFirst = false; //插入在最后面
                    } else {
                        isAdd = false;
                    }
                }
            } else {//其他中间状态
                isAdd = true;
            }

            //需要的则加入进来
            if (isAdd) {
                if (insertFirst) {
                    codeList.add(0, dbBaseInfo.getCode());
                } else {//无数据部分尽快先处理
                    codeList.add(dbBaseInfo.getCode());
                }
            }
        }
        log.info("DayPullCount本地筛选出需要同步code.size={}", codeList.size());
        return codeList;
    }

    @Override
    public List<String> listCodeByBasePullCount() {
        QueryWrapper<StockBaseInfo> wrapper = new QueryWrapper<>();
        wrapper.notIn("status", StockStatusEnum.OTHER.getCode()); //异常状态的数据不需要更新的
        wrapper.orderByDesc("id");
        List<String> codeList = new LinkedList<>();
        List<StockBaseInfo> tmpList = stockBaseInfoMapper.selectList(wrapper);
        for (StockBaseInfo dbBaseInfo : tmpList) {
            boolean isAdd = true;
            boolean insertFirst = true;

            Integer pullCount = dbBaseInfo.getBasePullCount();
            if (pullCount == null) {
                isAdd = true;
            } else if (pullCount <= -3) {
                //拉取多次都失败的,手动跳过即可,事后手动干预
                isAdd = false;
            } else if (pullCount >= 1) {
                //之前就拉取成功了,根据时间来判断时候,是否需要更新
                Date date = dbBaseInfo.getBaseUpdateTime();
                if (date == null) {
                    isAdd = true;
                } else {
                    Date splitDate = DateExtUtil.addDay(new Date(), -15);
                    if (DateExtUtil.compareDateTime(splitDate, date) >= 0) {
                        isAdd = true;
                        insertFirst = false; //插入在最后面
                    } else {
                        isAdd = false;
                    }
                }
            } else {//其他中间状态
                isAdd = true;
            }

            //需要的则加入进来
            if (isAdd) {
                if (insertFirst) {
                    codeList.add(0, dbBaseInfo.getCode());
                } else {//无数据部分尽快先处理
                    codeList.add(dbBaseInfo.getCode());
                }
            }
        }
        log.info("BasePullCount本地筛选出需要同步code.size={}", codeList.size());
        return codeList;
    }

    @Override
    public Set<String> listAllCode() {
        QueryWrapper<StockBaseInfo> wrapper = new QueryWrapper<>();
        wrapper.select("code");

        List<StockBaseInfo> tmpList = stockBaseInfoMapper.selectList(wrapper);
        Set<String> tmpSet = tmpList.stream().map(e -> e.getCode()).collect(Collectors.toSet());

        return tmpSet;
    }

    /**
     * 下载所有股票编码
     *
     * @return
     */
    @Override
    public Set<String> downAllCode() {
        Date date = new Date();
        List<StockBaseInfo> insertlist = new LinkedList<>();

        //1.查询数据存在的数据
        Set<String> dbList = listAllCode();

        //2.下载上海数据
        try {
            List<InfoShNameCode> tmpList = akService.stock_info_sh_name_code();
            tmpList.forEach(e -> {
                String code = e.getCode();
                if (!dbList.contains(code)) {
                    StockBaseInfo record = new StockBaseInfo();
                    record.setCodePrefix3(code.substring(0, 3));
                    record.setCode(code);
                    record.setName(e.getName());
                    record.setBasePullCount(0);
                    record.setBaseUpdateTime(date);
                    if ("688".equals(record.getCodePrefix3())) {
                        record.setStatus(StockStatusEnum.RM_HISTORY.getCode());//设置为不要历史的状态
                    } else {
                        record.setStatus(StockStatusEnum.NORMAL.getCode());//默认为正常
                    }
                    insertlist.add(record);
                }
            });
        } catch (Exception e) {
            log.error("上海数据下载异常", e);
        }

        //3.下载深圳数据
        try {
            List<InfoSzNameCode> tmpList = akService.stock_info_sz_name_code();
            tmpList.forEach(e -> {
                String code = e.getCode();
                if (!dbList.contains(code)) {
                    StockBaseInfo record = new StockBaseInfo();
                    record.setCodePrefix3(code.substring(0, 3));
                    record.setCode(code);
                    record.setName(e.getName());
                    record.setBasePullCount(0);
                    record.setBaseUpdateTime(date);
                    record.setStatus(StockStatusEnum.NORMAL.getCode());//默认为正常
                    insertlist.add(record);
                }
            });
        } catch (Exception e) {
            log.error("深圳数据下载异常", e);
        }

        //4.下载北京的数据-----
        try {
            List<InfoBjNameCode> tmpList = akService.stock_info_bj_name_code();
            tmpList.forEach(e -> {
                String code = e.getCode();
                if (!dbList.contains(code)) {
                    StockBaseInfo record = new StockBaseInfo();
                    record.setCodePrefix3(code.substring(0, 3));
                    record.setCode(code);
                    record.setName(e.getName());
                    record.setBasePullCount(-10);//默认为其他
                    record.setBaseUpdateTime(date);
                    record.setStatus(StockStatusEnum.RM_HISTORY.getCode());//默认全部不要历史
                    insertlist.add(record);
                }
            });
        } catch (Exception e) {
            log.error("北京数据下载异常", e);
        }

        //6.数据分组入库
        if (insertlist.size() > 0) {
            int batchId = 0;
            List<List<StockBaseInfo>> lists = CollectionUtil.splitList(insertlist, 500);
            for (List<StockBaseInfo> itemList : lists) {
                log.info("downAllCode批量插入batchId={},size={}", ++batchId, itemList.size());
                int counti = stockBaseInfoMapper.insertbatch(itemList);
            }
        }

        //6.数据去重---删除历史新记录,保留老id的
        int countd = stockBaseInfoMapper.deleteRepeatRetainMinId();

        Set<String> reset = insertlist.stream().map(e -> e.getCode()).collect(Collectors.toSet());
        return reset;
    }

    /**
     * 更新 list里面所有的code
     *
     * @param codeList
     * @return
     */
    @Override
    public List<StockBaseInfo> downAndUpdateStockInfo(Collection<String> codeList) {
        if (CollectionUtils.isEmpty(codeList)) {
            return null;
        }

        //0.调用失败的list暂存
        List<String> errList = new LinkedList<>();

        //1.下载某个list
        List<StockBaseInfo> reList = new LinkedList<>();
        for (String code : codeList) {
            try {
                ProfileCninfo profileCninfo = akService.stock_profile_cninfo(code);
                StockBaseInfo record = new StockBaseInfo();
                record.setCode(code);
                record.setBaseUpdateTime(new Date());
                if (profileCninfo != null) {
                    record.setIndustry(profileCninfo.getIndustry());
                    record.setBusinessScope(profileCninfo.getBusinessScope());
                    record.setCompanyName(profileCninfo.getCompanyName());
                    record.setName(profileCninfo.getName());
                    record.setEstablishTime(profileCninfo.getEstablishTime());
                    record.setListingTime(profileCninfo.getListingTime());
                    record.setLegalPersonName(profileCninfo.getLegalPersonName());
                    record.setRegisterAddress(profileCninfo.getRegisterAddress());
                    record.setRegisterCapital(profileCninfo.getRegisterCapital());
                    record.setExchangeMarket(profileCninfo.getExchangeMarket());
                    record.setMainBusiness(profileCninfo.getMainBusiness());
                    record.setOfficeAddress(profileCninfo.getOfficeAddress());
                    record.setNameFc(PinYinUtil.spellFirstCharSkipAlpha(profileCninfo.getName()));//设置中文

                    String symbol = null;
                    String exchangeMarket = profileCninfo.getExchangeMarket();
                    if (exchangeMarket.contains("北")) {
                        symbol = "bj" + code;
                    } else if (exchangeMarket.contains("上")) {
                        symbol = "sh" + code;
                    } else if (exchangeMarket.contains("深")) {
                        symbol = "sz" + code;
                    }
                    record.setSymbol(symbol);

                    record.setBasePullCount(1); //数据标记为成功
                    errorCount.set(0); //清0
                } else {
                    Integer tmpPullStatus = stockBaseInfoMapper.getBasePullCountByCode(code);
                    if (tmpPullStatus == null) {
                        tmpPullStatus = -1;
                    } else {
                        //之前是1,说明数据完整
                        if (tmpPullStatus == 1) {
                            tmpPullStatus = 2;//第二次拉取失败了
                        } else {
                            tmpPullStatus = (tmpPullStatus - 1);
                        }
                    }

                    record.setBasePullCount(tmpPullStatus);//数据标记为失败--其值表示拉取失败的次数

                    errorCount.addAndGet(1);//失败就加1
                    errList.add(code);
                }

                //执行更新语句
                stockBaseInfoMapper.updateByCode(record);

                //安全跳出----一直拉取数据会被警用ip
                if (errorCount.get() >= 3) {
                    log.error("程序即将中断,连续调用多次异常,次数={}", errorCount.get());
                    break;
                }

                //调用延迟---避开ip检测与屏蔽
                int sleepTime = 1000;
                if (profileCninfo == null) {
                    sleepTime = 3000;
                }
                Thread.sleep(sleepTime);//调用异常就延迟的长一点

            } catch (Exception e) {
                //记录失败的任务
                log.error("股票详情数据下载异常,code={}", code, e);
            }
        }

        if (!CollectionUtils.isEmpty(errList)) {
            String errStr = String.join(",", errList);
            log.error("程序即将中断,连续调用多次异常codes={}", errStr);
        }

        return reList;
    }

    @Override
    public Page pageList(StockBaseInfoPageReq req) {
        PageHelper.startPage(req.getPageNum(), req.getPageSize());
        QueryWrapper<StockBaseInfo> queryWrapper = new QueryWrapper();
        if (StringUtils.isNotBlank(req.getCode())) {
            queryWrapper.like("code", req.getCode());
        }
        if (StringUtils.isNotBlank(req.getCompanyName())) {
            queryWrapper.like("company_name", req.getCompanyName());
        }
        if (StringUtils.isNotBlank(req.getIndustry())) {
            queryWrapper.like("industry", req.getIndustry());
        }
        if (StringUtils.isNotBlank(req.getIndustryName())) {
            queryWrapper.like("industry_name", req.getIndustryName());
        }
        if (StringUtils.isNotBlank(req.getName())) {
            queryWrapper.like("name", req.getName());
        }
        if (StringUtils.isNotBlank(req.getNameFc())) {
            queryWrapper.like("name_fc", req.getNameFc());
        }
        queryWrapper.orderByDesc("id");
        List<StockBaseInfo> list = stockBaseInfoMapper.selectList(queryWrapper);
        return new Page<>(list, new PageInfo<>(list).getTotal(), req);
    }
}
